import re
import json
import logging

from django.views import View
from django.urls import reverse
from django.contrib.auth import login, logout
from django_redis import get_redis_connection
from django.shortcuts import render, redirect
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth import authenticate, login, logout
from django.http.response import JsonResponse, HttpResponseForbidden, HttpResponseBadRequest

from apps.users import constants
from utils.secret import SecretOauth
from celery_tasks.email.tasks import send_verify_email
from apps.users.models import User, Address
from utils.response_code import RETCODE
from meiduo_mall.settings.dev import logger
from apps.users.utils import generate_verify_email_url


# 修改密码
class ChangePasswordView(LoginRequiredMixin, View):
    def get(self, request):
        return render(request, "user_center_pass.html")

    def post(self, request):

        # 1, 接收参数
        old_pwd = request.POST.get("old_pwd")
        new_pwd = request.POST.get("new_pwd")
        new_cpwd = request.POST.get("new_cpwd")
        # 2, 判空
        if not all([old_pwd, new_cpwd, new_pwd]):
            return HttpResponseForbidden('缺少必传参数')
        # 3, 判断密码是否正确
        try:
            request.user.check_password(old_pwd)
        except Exception as e:
            logger.error(e)
            return render(request, 'user_center_pass.html', {'origin_pwd_errmsg': '原始密码错误'})
        # 4, 修改密码
        try:
            request.user.set_password(new_pwd)
            request.user.save()
        except Exception as e:
            logger.error(e)
            return render(request, 'user_center_pass.html', {'origin_pwd_errmsg': '修改密码失败'})
        # 5, 退出登录状态
        logout(request)

        # 6, 重定向到登录页面
        response = redirect(reverse("users:login"))

        # 返回前端响应
        return response



# 13 . 修改标题
class UpdateTitleAddressView(View):
    def put(self, request, address_id):

        # 1.获取title  Json
        title = json.loads(request.body.decode()).get('title')

        try:
            # 2. 根据id获取 address
            address = Address.objects.get(id=address_id)
            # 3. 修改  address.title = title
            address.title = title
            # 4. save()
            address.save()

        except Exception as e:
            logger.error(e)
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '设置地址标题失败'})

        return JsonResponse({'code': RETCODE.OK, 'errmsg': '设置地址标题成功'})


# 12 .设置默认地址
class DefaultAddressView(View):
    def put(self, request, address_id):

        try:
            # 1.根据id查地址
            address = Address.objects.get(id=address_id)

            # 2.修改当前用户的默认地址 default_address
            request.user.default_address = address

            # 3. save()
            request.user.save()
        except Exception as e:
            logger.error(e)
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '设置默认地址失败'})

        return JsonResponse({'code': RETCODE.OK, 'errmsg': '设置默认地址成功'})


# 11.修改地址
class UpdateAddressView(View):
    # 修改
    def put(self, request, address_id):

        # 1.拼接参数, jSOn参数
        json_dict = json.loads(request.body.decode())
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 2.校验,判空正则

        # 3.修改 address_id 对应的 地址的属性
        try:
            address = Address.objects.get(id=address_id)
            address.user = request.user
            address.title = receiver
            address.receiver = receiver
            address.province_id = province_id
            address.city_id = city_id
            address.district_id = district_id
            address.place = place
            address.mobile = mobile
            address.tel = tel
            address.email = email
            address.save()
        except Exception as e:
            logger.error(e)
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '更新地址失败'})

        # 4.构建前端需要的数据格式 { }
        address = Address.objects.get(id=address_id)
        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }

        # 响应更新地址结果
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '更新地址成功', 'address': address_dict})

    # 删除
    def delete(self, request, address_id):

        # 1, 获取参数所对应的对象
        address = Address.objects.get(id=address_id)
        # 2, 逻辑删除,把is_delete至为true
        try:
            address.is_deleted = True
            address.save()
        except Exception as e:
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '删除地址失败'})

            # 响应删除地址结果
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '删除地址成功'})

        # 3, 返回给前端响应


# 10, 新增收货地址
class CreateAddressView(LoginRequiredMixin, View):
    def post(self, request):

        # 新增,在接收参数之前先判断用户地址的个数,如果超过20个,就给用户提示
        count = Address.objects.filter(user=request.user, is_deleted=False).count()

        if count > 20:
            return JsonResponse({'code': RETCODE.THROTTLINGERR, 'errmsg': '超过地址数量上限'})

        # 1.接收参数 JSON
        json_dict = json.loads(request.body.decode())
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 2.校验 判空not all[], 正则

        # 3.入库
        try:
            address = Address.objects.create(
                user=request.user,
                title=receiver,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email,
            )

            # 判断用户是否有默认地址,如果没有,就绑定一个
            if not request.user.default_address:
                request.user.default_address = address
                request.user.save()
        except Exception as e:
            logger.error(e)
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '新增地址失败'})

        # 4.构建前端要的数据json ; dict
        address_dict = {
            "id": address.id,
            "title": address.receiver,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email,
        }
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '新增地址成功', 'address': address_dict})


# 9, 展示收货地址
class AddressView(View):
    def get(self, request):
        # 1, 获取当前用户没有被逻辑删除的地址
        address_object = Address.objects.filter(user=request.user, is_deleted=False)
        # 2, 构建列表,存放所有的地址信息
        address_list = []
        for address in address_object:
            address_list.append({
                "id": address.id,
                "title": address.title,
                "receiver": address.receiver,
                "province": address.province.name,
                "city": address.city.name,
                "district": address.district.name,
                "place": address.place,
                "mobile": address.mobile,
                "tel": address.tel,
                "email": address.email
            })

        # 3, 构建前端需要的数据
        context = {
            "default_address_id": request.user.default_address_id,
            "addresses": address_list
        }

        # 4, 返回前端数据
        return render(request, "user_center_site.html", context)


# 8, 处理邮箱的回调函数
class VerifyEmailView(View):
    def get(self, request):
        # 1, 获取token
        secret_token = request.GET.get("token")
        # 2, 效验token
        if not secret_token:
            return HttpResponseBadRequest("token不存在")
        # 3, 解密token
        token = SecretOauth().loads(secret_token)
        # 4, 调用数据库,看是否存在此用户
        try:
            user = User.objects.get(id=token["user_id"], email=token["email"])
        except Exception as e:
            logger.error(e)
        # 5, 保存数据
        try:
            user.email_active = True
            user.save()
        except Exception as e:
            logger.error(e)
        # 6, 冲定向到首页
        print("重定向到首页")
        return redirect(reverse("contents:index"))


# 7, 添加用户的email地址
class EmailView(LoginRequiredMixin, View):
    def put(self, request):
        # 1, 接受前端传递的email参数
        json_email = request.body.decode()
        json_dict = json.loads(json_email)
        email = json_dict.get("email")
        # 2, 校验参数

        # 3, 保存到数据库中
        try:
            request.user.email = email
            request.user.save()
        except Exception as e:
            return JsonResponse({'code': RETCODE.EMAILERR, 'errmsg': '添加邮箱失败'})

        # 调用函数,生成邮件的回调函数
        verify_url = generate_verify_email_url(request.user)
        # 调用函数,发送邮件,传递的参数是邮箱和回调函数
        send_verify_email.delay(email, verify_url)

        # 4, 返回前端响应
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '添加邮箱成功'})


# 6, 个人中心页面
"""
功能:
    展示个人中心页面
原理:
    在用户未登录的情况下,使用django自定义的扩展类进行重定向到login页
    此时需要设置login_url,在全局配置中设置LOGIN_URL="/login/"
    这样就可以直接使用django自带的认证系统重定向到登录页了
    如果已经登录,则可以直接访问个人中心页
    另外,正常情况下,当用户点击个人中心的时候,会携带user,
    我们可以在数据库中查找是否有这个user,这样也可以判断用户是否登录
    只不过我们现在使用django自带的登录认证系统来做了
"""


class UserInfoView(LoginRequiredMixin, View):
    def get(self, request):
        # 1.user_id --cookie取出username, 将数据发给前端进行展示
        # 2. request.user
        context = {
            'username': request.user.username,
            'mobile': request.user.mobile,
            'email': request.user.email,
            'email_active': request.user.email_active,
        }
        return render(request, "user_center_info.html", context)


# 5, 退出登录功能
"""
"""


class LogoutView(View):
    def get(self, request):
        # 退出,清空session
        logout(request)

        response = render(request, "login.html")
        # 删除cookie中的username
        response.delete_cookie("username")
        return response


# 4, 登录功能
"""
功能:
    显示登录页面
"""


class LoginView(View):
    def get(self, request):
        return render(request, 'login.html')

    def post(self, request):

        # 1, 接收参数
        username = request.POST.get("username")
        password = request.POST.get("password")
        remembered = request.POST.get("remembered")

        # 2, 效验参数完成性
        if not all([username, password]):
            return HttpResponseForbidden("参数不全")

        # 2.1 效验用户名:
        if not re.match(r"^[a-zA-Z0-9_-]{5,20}$", username):
            return HttpResponseForbidden("请输入5-20个字符的用户名!")

        # 2.2 效验密码:
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return HttpResponseForbidden('请输入8-20位的密码')

        # 3, 使用django自带的认证系统进行用户名和密码的验证
        user = authenticate(request, username=username, password=password)

        # 4, 判断用户是否存在
        if user is None:
            return render(request, "login.html", {'account_errmsg': '用户名或密码错误'})

        # 5, 保存登录状态
        login(request, user)

        # 6, 是否记住用户名
        if remembered != "on":
            # 不记住用户名,过期时间为0
            request.session.set_expiry(0)
        else:
            # 记住用户名,过期时间是2周
            request.session.set_expiry(None)

        # 判断用户是点击了个人中心之后由于没有登录才跳转到登录页面的,我们通过next进行判断,
        # 发现有next参数之后,在用户点击登录之后,我们直接跳转到个人中心,也就是next参数所代表的url
        next = request.GET.get("next")
        if next:
            response = redirect(next)
        else:
            # 返回响应结果
            response = redirect(reverse("contents:index"))
        response.set_cookie("username", username, constants.USERNAME_EXPIRE_TIME)
        return response


# 3, 注册功能
"""
功能:
    实现注册功能
触发时刻:
    输入网址,并回车
请求方式:
    POST
请求url:
    0, /register
流程:
    1, 获取请求体中的参数,分别是username, password, password2, mobile, allow, msg_code
    2, 通过all方法,判断请求体中的参数完整性
    3, 通过正则效验参数是否合法
    4, 效验完毕,保存到mysql数据库中
    5, 使用login方法进行状态保存
    6, 使用redirect方法配合路由的反向解析重定向到首页
"""


class RegisterView(View):
    def get(self, request):
        return render(request, "register.html")

    def post(self, request):

        # 接收参数:
        username = request.POST.get("username")
        password = request.POST.get("password")
        password2 = request.POST.get("password2")
        mobile = request.POST.get("mobile")
        allow = request.POST.get("allow")
        # 补充的参数(用户输入的验证码)
        sms_code = request.POST.get("msg_code")

        # 效验参数
        # 1, 效验参数是否完整
        if not all([username, password, password2, mobile, allow]):
            return HttpResponseForbidden("参数不齐")

        # 效验参数是否合法
        # 2.1, 效验username是否合法
        if not re.match("^[a-zA-Z0-9_-]{5,20}$", username):
            return HttpResponseForbidden("请输入5-20个字符的用户名1")

        # 2.2 效验密码是否合法
        if not re.match("^[0-9A-Za-z]{8,20}$", password):
            return HttpResponseForbidden("请输入8-20密码")

        # 2.3 效验密码是否一致
        if password != password2:
            return HttpResponseForbidden("两次密码不一致")

        # 2.4 效验手机号码是否合法
        if not re.match("^1[345789]\d{9}$", mobile):
            return HttpResponseForbidden("您输入的手机号格式不正确")

        # 2.5 判断是否勾选同意协议
        if allow != "on":
            return HttpResponseForbidden("请勾选用户协议")

        # 补充: 对比短信验证码,
        try:
            redis_code_client = get_redis_connection("sms_code")
            redis_code = redis_code_client.get("sms_%s" % mobile)

            if not redis_code:
                return render(request, "register.html", {"sms_code_errmsg": "验证码已失效"})

            if sms_code != redis_code.decode():
                return render(request, "register.html", {"sms_code_errmsg": "验证码错误"})
        except Exception as e:
            logger(e)
        # 3, 保存数据到数据库(进行捕获异常)
        try:
            user = User.objects.create_user(username=username, password=password, mobile=mobile)
        except Exception as e:
            logging.error(e)
            return render(request, "register.html", {'register_message': '注册失败'})
        # 4, 保存当前用户的登录状态(保存在session中)
        login(request, user)

        # 5, 返回相应对象(重定向到首页)
        response = redirect("contents:index")
        response.set_cookie("username", username, constants.USERNAME_EXPIRE_TIME)
        return response


# 2, 验证手机号
"""
功能:
    判断手机号是否已经注册
触发时刻:
    在前端的mobile字段失去焦点的时候触发check_mobile函数发送该url
请求方法:
    GET
请求url:
    0, /mobiles/(?P<mobile>1[3-9]\d{9})/count/
流程:
    1, 获取查询字符串中的参数mobile,并且通过正则进行效验
    2, 通过mobile参数,在数据库中进行获取,并返回查询到的条目数
    3, 返回前端json数据, 传递主要数据count,前端通过count来判断手机号是否注册
    4, 如果条目数为0,则手机号未被注册,如果条目数为1,则手机号已经被注册
"""


class MobileCountView(View):
    def get(self, request, mobile):
        count = User.objects.filter(mobile=mobile).count()

        dict = {
            "code": RETCODE.OK,
            "errmsg": "OK",
            "count": count
        }
        return JsonResponse(dict)


# 1, 验证用户名
"""
功能:
    判断用户名是否已经注册
触发时刻:
    在前端的username字段失去焦点的时候触发check_username函数发送该url
请求方法:
    GET
请求url:
    0, /usernames/(?P<username>[a-zA-Z0-9_-]{5,20})/count/
流程:
    1, 获取查询字符串中的参数username,并且通过正则进行效验
    2, 通过username参数,在数据库中进行获取,并返回查询到的条目数
    3, 返回前端json数据, 传递主要数据count,前端通过count来判断用户名是否注册
    4, 如果条目数为0,则用户名未被注册,如果条目数为1,则用户名已经被注册
"""


class UsernameCountView(View):
    def get(self, request, username):
        # 1, 正则效验

        # 2, 数据库中查找数据
        count = User.objects.filter(username=username).count()

        dict = {
            "code": RETCODE.OK,
            "errmsg": "OK",
            "count": count
        }
        # 返回json对象
        return JsonResponse(dict)
